/*
 * Author: vyouzhi <vyouzhi@163.com>
 * http://www.xda.cn
 *
 * File: socketbase.c
 * Create Date: 2011-08-05 20:20:44
 *
 */
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netinet/tcp.h>
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>

#include "socket_lib.h"
#include "conf_lib.h"


int SetNonBlocking(int s){
    int opts;
    opts=fcntl(s, F_GETFL);
    if(opts == -1)
    {
        perror("fcntl(sock,GETFL)");
        exit(-1);
    }
    opts |= O_NONBLOCK;
    if(fcntl(s, F_SETFL, opts) == -1)
    {
        perror("fcntl(sock,SETFL,opts)");
        exit(1);
    }
	return 0;
}

int Set_SockOpt(int sockfd){
	int reuse=1;	
	int sock_buf_size = 512;
	struct linger opt;

	if (-1 == setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &reuse,
		sizeof(int))) {
		printf("Couldn't setsockopt(SO_REUSEADDR)\n");
		return (-1);
	}
	
	if (-1 == setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, (char *)&sock_buf_size,
		sizeof(sock_buf_size) )) {
		printf("Couldn't setsockopt(SO_SNDBUF)\n");
		return (-1);
	}

	if (-1 == setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, (char *)&sock_buf_size,
		sizeof(sock_buf_size) )) {
		printf("Couldn't setsockopt(SO_RCVBUF)\n");
		return (-1);
	}
	
	opt.l_onoff = 1;
	opt.l_linger = 60;

	if (-1 == setsockopt(sockfd, SOL_SOCKET, SO_LINGER, &opt,
		sizeof(struct linger))){
		printf("Coundn't setsockopt(SO_LINGER)\n");
		return (-1);
	}

	return 0;
}

int Socket_Init(void){

	int sockfd, port, max_link;
	struct sockaddr_in   server_addr;
	char *host;
	
	host = conf_get("server_ip");
	port = atoi(conf_get("server_port"));
	max_link = atoi(conf_get("max_link"));

	bzero(&server_addr, sizeof(server_addr));
	inet_pton(AF_INET, host, &(server_addr.sin_addr));
	server_addr.sin_family = AF_INET;
	server_addr.sin_port = htons(port);

	sockfd = socket(AF_INET, SOCK_STREAM, 0);
	
	if (-1 == sockfd) {return(-1);}
	
	if (-1 == Set_SockOpt(sockfd)) {
		close(sockfd);
		return (-1);
	}

	if (-1 == bind(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr))) {
		close(sockfd);
		return(-1);
	}
	/*	
	if (-1 == SetNonBlocking(sockfd)){
		close(sockfd);
		return (-1);
	}*/
	if (-1 == listen(sockfd, max_link)) {
		close(sockfd);
		return(-1);
	}
	
	return (sockfd);
}

int Client_Init(char *host, int port){
	int flag = 1, client_fd;
	struct sockaddr_in to_server_addr;

    if((client_fd = socket(AF_INET , SOCK_STREAM , 0)) == - 1) {
        perror("socket error") ;
        return (-1);
    }

    memset(&to_server_addr, 0, sizeof(struct sockaddr)) ;
    to_server_addr.sin_family = AF_INET;
    to_server_addr.sin_port = htons(port);
    to_server_addr.sin_addr.s_addr = inet_addr(host) ;

	if (setsockopt(client_fd, IPPROTO_TCP, TCP_NODELAY,
                  &flag, sizeof(flag)) == -1) {
    	perror("Failed to set TCP_NODELAY");
		return (-1);
    }

	if(connect(client_fd, (struct sockaddr *)&to_server_addr, sizeof(struct sockaddr)) == - 1) {
        perror("connect error") ;
        return (-1);
    }
	return client_fd;
}

int Content_Length(char *Buf){
	char *len_find, *cBuf;
	char find_word[] = "Content-Length:";
	int c_len;

	if(strlen(Buf) <= strlen(find_word)) return (-1);
	cBuf = strdup(Buf);
	len_find = strstr(cBuf, find_word);
	if(len_find != 0){
		len_find = strtok(len_find, "\r\n");
	}
	if(len_find != NULL){
		len_find = strstr(len_find, " ");
	}

	if(len_find == NULL){
		free(cBuf);
		return (-1);
	}
	c_len = atoi(len_find);
	if(c_len == 0){
		free(cBuf);
		return (-1);
	}
	free(cBuf);
	return c_len;
}

ssize_t Socket_Recv(int c_fd, char **recvBuf){
	ssize_t nread, stop, len_recv, off_set;
	char sBuf[MAXBUFFSIZE], tBuf[8129];

	off_set = 0; 
	stop = 0;
	len_recv = 0;
	
	/*sBuf[0] = '\0';*/
	while(off_set < MAXBUFFSIZE && !stop){
		nread = recv(c_fd, tBuf, 8129, 0);
	/*	printf("sbuf %s\n", sBuf);
		printf("nread %d\n", nread);*/
		tBuf[nread] = '\0';
		if (nread == -1){
			if (errno != EINTR)
				return (-1);
		}
		else if(nread == 0){
			stop = 1;
		}
		else if(nread > 0){
			if(len_recv == 0){
				len_recv = Content_Length(tBuf);
				if(len_recv == -1) return (-1);
				off_set = 0;
			}
			
			strcpy(sBuf+off_set, tBuf);
			off_set += nread;
	
			if(off_set >= len_recv) {
				stop = 1;
			}
		}else return (-1);
	}

	*recvBuf = sBuf;

	return off_set;
}

int Socket_Send(int c_fd, const char *sendBuf, int buflen){
	int tmp, s_len, total = buflen;
	
	while(total > 0 && sendBuf != NULL){
		tmp = send(c_fd, sendBuf, total, 0);

		if(tmp < 0)
		{
			if(errno == EINTR)
				break;
			if(errno == EAGAIN)
			{
				sleep(1);
				printf("i sleep 1 s\n");
				continue;
			}
		}	
		s_len += tmp;
		total -= tmp;
		sendBuf += (size_t)tmp;
		/*printf("send tmp is %d\n", tmp);
		p += tmp;*/
	}
	return s_len;
}

void Socket_Close(int sock_fd){
	close(sock_fd);
}

/* vim: set ts=4 sw=4: */

